
package com.yxd.bdmapdemo.utils;

import android.annotation.SuppressLint;
import android.content.Context;
import android.os.Environment;
import android.text.TextUtils;
import android.util.Log;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * Log增强工具
 * <p/>
 * 你只需要填入想输出的内容,繁琐的东西自动帮你补全<br>
 * 1.自动用类名.方法名填充TAG<br>
 * 2.自动填充文件名,行号<br>
 * 3.Eclipse里面双击Logcat的输出,能转跳到Java文件相应行<br>
 * 4.能读取配置文件决定是否输出Logcat和Log文件,无需重新编译工程.<br>
 * 5.可以在配置文件配置密码,如果密码不符,则无法更改Log设置
 * <p/>
 * 性能方面,耗时是{@link Log}的20倍
 * <p/>
 * 使用示例:
 * <p/>
 * <pre>
 * L.i(&quot;闪电狗&quot;);
 *
 * LogCat输出:
 * 09-06 16:21:24.262: I/TestLogActivity$2.onClick(3212): 闪电狗
 * 09-06 16:21:24.262: I/TestLogActivity$2.onClick(3212):  at me.fantouch.demo.TestLogActivity$2.onClick(TestLogActivity.java:48)
 * </pre>
 * <p/>
 *
 * @author Fantouch
 */
@SuppressLint("SimpleDateFormat")
public class L {
    private static final String TAG = L.class.getSimpleName();

    public static final String DEBUG    = "Debug模式";
    public static final String INFO     = "Info模式";
    public static final String WARN     = "警告模式";
    public static final String ERROR    = "错误模式";
    public static final String sys      = "系统模式";//系统Log
    public static final String abnormal = "异常模式"; //异常Log

    public static final String TYPE_APPLICATION     = "应用类型";//应用类型
    public static final String TYPE_SYSTEM          = "系统类型"; //系统日志
    public static final String TYPE_ABNORMAL        = "异常类型"; //异常日志
    //错误类型 1 Debug 2 Info 3 Warn 4 error 5 system 6 abnormal
    private static      boolean isToLogcatByApp      = true; //应用Log是否打印
    private static      boolean isToLogcatBySys      = false; //系统Log是否打印
    private static      boolean isToLogcatByAbnormal = false; //异常Log是否打印
    private static      boolean isToFile             = false;

    private L() {/* 禁止实例化 */
    }


    public static void i() {
        log(INFO, null, null);
    }

    public static void i(String msg) {
        log(INFO, msg, null);
    }

    public static void i(Throwable throwable) {
        log(INFO, null, throwable);
    }

    public static void i(String msg, Throwable throwable) {
        log(INFO, msg, throwable);
    }

    public static void d() {
        log(DEBUG, null, null);
    }

    public static void d(String msg) {
        log(DEBUG, msg, null);
    }

    public static void d(Throwable throwable) {
        log(DEBUG, null, throwable);
    }

    public static void d(String msg, Throwable throwable) {
        log(DEBUG, msg, throwable);
    }

    public static void w() {
        log(WARN, null, null);
    }

    public static void w(String msg) {
        log(WARN, msg, null);
    }

    public static void w(Throwable throwable) {
        log(WARN, null, throwable);
    }

    public static void w(String msg, Throwable throwable) {
        log(WARN, msg, throwable);
    }

    public static void e() {
        log(ERROR, null, null);
    }

    public static void e(String msg) {
        log(ERROR, msg, null);
    }

    public static void e(Throwable throwable) {
        log(ERROR, null, throwable);
    }

    public static void e(String msg, Throwable throwable) {
        log(ERROR, msg, throwable);
    }

    public static void s() {
        log(sys, null, null);
    }

    public static void s(String msg) {
        log(sys, msg, null);
    }

    public static void s(Throwable throwable) {
        log(sys, null, throwable);
    }

    public static void s(String msg, Throwable throwable) {
        log(sys, msg, throwable);
    }

    public static void a() {
        log(abnormal, null, null);
    }

    public static void a(String msg) {
        log(abnormal, msg, null);
    }

    public static void a(Throwable throwable) {
        log(abnormal, null, throwable);
    }

    public static void a(String msg, Throwable throwable) {
        log(abnormal, msg, throwable);
    }

    private static final String LOG_FILE_EXTENSION = ".log";

    /**
     * log文件路径
     */
    private static String filePath = "";

    private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd");

    private static final SimpleDateFormat DATE_TIME_FORMAT =
            new SimpleDateFormat("[yyyy-MM-dd hh:mm:ss] ");

    private static void log(String logLevel, String msg, Throwable throwable) {

        if (isLogcatEnabled() || isLogToFileEnabled()) {

            StackTraceElement element = Thread.currentThread().getStackTrace()[4];
            String tag = "info";
            //来源
            String codeLocation = getCodeLocation(element);
            String message = msg;
            //时间
            String time = DATE_TIME_FORMAT.format(new Date(System.currentTimeMillis()));
            msg += codeLocation;
            String type = judgeLevel(logLevel);
            if (!TextUtils.isEmpty(type)) {
                //根据当前状态可以打印
                switch (logLevel) {

                    case INFO:
                        Log.i(tag, msg);
                        // com.readystatesoftware.notificationlog.L.i(tag, msg, throwable);
                        break;

                    case DEBUG:
                        Log.d(tag, msg, throwable);
                        // com.readystatesoftware.notificationlog.Log.d(tag, msg, throwable);
                        break;

                    case WARN:
                        Log.w(tag, msg, throwable);
                        // com.readystatesoftware.notificationlog.Log.w(tag, msg, throwable);
                        break;

                    case ERROR:
                        Log.e(tag, msg, throwable);
                        // com.readystatesoftware.notificationlog.Log.e(tag, msg, throwable);
                        break;
                    case sys:
                        Log.w(tag, msg, throwable);
                        break;
                    case abnormal:
                        Log.w(tag, msg, throwable);
                        break;
                    default:
                        break;
                }
                //存入数据库
              /*  if (isLogToFileEnabled()) {
                    AppLogEntity log = new AppLogEntity();
                    log.setCreateTime(time);
                    log.setFrom(codeLocation);
                    log.setInfo(message);
                    log.setLevel(logLevel);
                    log.setType(type);
                    log.setUpdatestate(0); //设置此日志未同步
                    com.sxun.chargepile.DB.log.DBInterface.instance().insertOrReplace(log);
                }*/
            }
            //
            //            if (isLogToFileEnabled()) {
            //                String time = DATE_TIME_FORMAT.format(new Date(System.currentTimeMillis()));
            //                writeFile(time, "来自:"+tag, "错误信息:"+msg + Log.getStackTraceString(throwable)+"\r\n");
            //            }

        }

    }

    /**
     * 描述:根据等级，判断日志开个是否开启
     */
    private static String judgeLevel(String logLevel) {
        switch (logLevel) {
            case DEBUG:
            case INFO:
            case ERROR:
            case WARN:
                if (isToLogcatByApp)
                    //如果是应用Log并且开关是开着的，则让其打印
                    return TYPE_APPLICATION;
                break;
            case sys:
                if (isToLogcatBySys)
                    //如果是系统Log并且开关是开着的，则让其打印
                    return TYPE_SYSTEM;
                break;
            case abnormal:
                if (isToLogcatByAbnormal)
                    //如果是异常Log并且开关是开着的，则让其打印
                    return TYPE_ABNORMAL;
                break;
        }
        return "";
    }

    /**
     * @return 格式类似于: TestLogActivity.onClick
     */
    private static String getTag(StackTraceElement stackTraceElement) {
        return stackTraceElement.getClassName().substring(
                stackTraceElement.getClassName().lastIndexOf(".") + 1) + "."
                + stackTraceElement.getMethodName() + "()";
    }

    /**
     * @return 格式类似于: at me.fantouch.demo.TestLogActivity$2.onClick(TestLogActivity.java:47) <br>
     * 这样的格式可以实现eclipse双击转跳到源码相应位置
     */
    private static String getCodeLocation(StackTraceElement stackTraceElement) {
        return "\t\tat "
                + stackTraceElement.getClassName()
                + "."
                + stackTraceElement.getMethodName()
                + "("
                + stackTraceElement.getFileName()
                + ":"
                + stackTraceElement.getLineNumber()
                + ")";
    }


    /**
     * 设置是否把应用日志输出到Logcat,建议在Application里面设置
     *
     * @param enable 缺省false
     */
    public static void setLogcatEnableByApp(boolean enable) {
        isToLogcatByApp = enable;
    }

    /**
     * 设置是否把系统日志输出到Logcat,建议在Application里面设置
     *
     * @param enable 缺省false
     */
    public static void setLogcatEnableBySys(boolean enable) {
        isToLogcatBySys = enable;
    }

    public static boolean isToLogcatByApp() {
        return isToLogcatByApp;
    }

    public static void setIsToLogcatByApp(boolean isToLogcatByApp) {
        L.isToLogcatByApp = isToLogcatByApp;
    }

    public static boolean isToLogcatBySys() {
        return isToLogcatBySys;
    }

    public static void setIsToLogcatBySys(boolean isToLogcatBySys) {
        L.isToLogcatBySys = isToLogcatBySys;
    }

    public static boolean isToLogcatByAbnormal() {
        return isToLogcatByAbnormal;
    }

    public static void setIsToLogcatByAbnormal(boolean isToLogcatByAbnormal) {
        L.isToLogcatByAbnormal = isToLogcatByAbnormal;
    }

    /**
     * 设置是否把异常日志输出到Logcat,建议在Application里面设置
     *
     * @param enable 缺省false
     */
    public static void setLogcatEnableByAbnormal(boolean enable) {
        isToLogcatByAbnormal = enable;
    }


    public static boolean isLogcatEnabled() {
        return isToLogcatByApp;
    }

    /**
     * 设置是否保存日志到文件,文件所在目录示例/data/data/packageName/file/xx.log
     *
     * @param enable 缺省false
     */
    public static void setLogToFileEnable(boolean enable, Context ctx) {
        setLogToFileEnable(enable, ctx, null);
    }

    /**
     * 设置是否保存日志到文件,并指定文件路径
     *
     * @param enable 缺省false
     */
    public static void setLogToFileEnable(boolean enable, Context ctx, String path) {
        if (enable) {
            if (!isToFile) {
                isToFile = true;
                if (!TextUtils.isEmpty(path)) {
                    filePath = path;
                } else {
                    filePath = ctx.getFilesDir().getAbsolutePath();
                }
                Log.v(TAG, "Save Log To File Enabled");
            } else {
                Log.w(TAG, "Save To File Already Enabled");
            }
        } else {
            if (isToFile) {
                isToFile = false;
                Log.v(TAG, "Save Log To File Disabled");
            } else {
                Log.w(TAG, "Save To File Already Disabled");
            }
        }
    }

    public static boolean isLogToFileEnabled() {
        return isToFile;
    }


    private static void creatDefaultCfgFileIfNecessary(String password) {
        File file = null;
        try {
            file = getCfgFile();
        } catch (IOException e1) {
            e1.printStackTrace();
            return;
        }

        if (file.length() > 0)
            return;

        BufferedWriter writer = null;
        try {
            writer = new BufferedWriter(new FileWriter(file.getAbsolutePath(), false));
            if (password != null) {
                writer.append("password=\n");
            }
            writer.append("logcat=false\n");
            writer.append("logfile=false\n");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (writer != null) {
                try {
                    writer.close();
                } catch (IOException e) {
                }
            }
        }
    }

    // TODO 异步操作
    private static void writeFile(String time, String tag, String msg) {
        File file = null;
        try {
            file = getLogFile();
        } catch (IOException e1) {
            e1.printStackTrace();
            return;
        }

        BufferedWriter writer = null;
        BufferedReader reader = null;
        try {
            writer = new BufferedWriter(new FileWriter(file.getAbsolutePath(), true));
            reader = new BufferedReader(new InputStreamReader(
                    new ByteArrayInputStream(msg.getBytes())));

            boolean isFirstLoop = true;
            String line;
            while ((line = reader.readLine()) != null) {
                if (isFirstLoop) {
                    isFirstLoop = false;
                    writer.append(time);
                    writer.append(tag);
                    writer.append("\n");
                }

                if (!TextUtils.isEmpty(line)) {
                    //                    writer.append(DATE_TIME_PLACEHOLDER);
                }

                writer.append(line);

                if (!TextUtils.isEmpty(line)) {
                    writer.append("\n");
                }

            }
            writer.append("\r\n");

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (writer != null) {
                    writer.close();
                }
                if (reader != null) {
                    reader.close();
                }
            } catch (IOException e) {
            }
        }
    }

    private static File getLogFile() throws IOException {
        File file = new File(filePath,
                DATE_FORMAT.format(new Date(System.currentTimeMillis())) + LOG_FILE_EXTENSION);
        return creatFileIfNotExists(file);
    }

    private static File getCfgFile() throws IOException {
        File file = new File(Environment.getExternalStorageDirectory().getAbsoluteFile(), "log.cfg");
        return creatFileIfNotExists(file);
    }

    /**
     * @return null if IOException
     */
    private static File creatFileIfNotExists(File file) throws IOException {
        if (!file.exists()) {
            new File(filePath).mkdirs();
            file.createNewFile();
        }
        return file;
    }
}
